package uk.co.droidinactu.heartratemonitor;

import java.io.DataInputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Set;
import java.util.UUID;

import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.widget.ArrayAdapter;
import android.widget.TextView;

public class HeartRateMonitorActivity extends Activity {

	private class GpsLocationTask extends AsyncTask<Integer, Location, Integer> {

		private Boolean continueRunning = true;
		private LocationListener locationListener;
		private LocationManager locationManager;
		private android.location.Location myLocation;
		private android.location.Location oldLocation;

		@Override
		protected Integer doInBackground(final Integer... params) {
			// TODO Auto-generated method stub
			return null;
		}

		@Override
		protected void onPostExecute(final Integer result) {
			// TODO Auto-generated method stub
			super.onPostExecute(result);
		}

		@Override
		protected void onPreExecute() {
			super.onPreExecute();
			// Acquire a reference to the system Location Manager
			locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);

			// Define a listener that responds to location updates
			locationListener = new LocationListener() {
				@Override
				public void onLocationChanged(final android.location.Location location) {
					myLocation = location;
				}

				@Override
				public void onProviderDisabled(final String provider) {
				}

				@Override
				public void onProviderEnabled(final String provider) {
				}

				@Override
				public void onStatusChanged(final String provider, final int status, final Bundle extras) {
				}
			};

			// Register the listener final with the Location final Manager to
			// receive final location updates
			locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, locationListener);
			locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, locationListener);
		}

		@Override
		protected void onProgressUpdate(final Location... values) {
			// TODO Auto-generated method stub
			super.onProgressUpdate(values);
			runOnUiThread(new Runnable() {
				@Override
				public void run() {
					updateCurrentLocation(values[0]);
				}
			});
		}

		public void setContinueRunning(final Boolean value) {
			continueRunning = value;
		}
	}

	private class PolarCommsTask extends AsyncTask<String, HeartRateRecord, Integer> {

		private Boolean continueRunning = true;
		private HeartRateRecord hrr = new HeartRateRecord();
		private BluetoothSocket mmSocket = null;

		@Override
		protected Integer doInBackground(final String... params) {
			try {
				// MY_UUID is the app’s UUID string, also used by the server
				// code
				// mmSocket =
				// polarBtDev.createRfcommSocketToServiceRecord(MY_UUID);

				final Method m = polarBtDev.getClass().getMethod("createRfcommSocket",
						new Class[] { int.class });
				mmSocket = (BluetoothSocket) m.invoke(polarBtDev, 1);
			} catch (final SecurityException e) {
				Log.d(LOG_TAG, "createRfcommSocketToServiceRecord Exception : ", e);
				e.printStackTrace();
			} catch (final NoSuchMethodException e) {
				Log.d(LOG_TAG, "createRfcommSocketToServiceRecord Exception : ", e);
				e.printStackTrace();
			} catch (final IllegalArgumentException e) {
				Log.d(LOG_TAG, "createRfcommSocketToServiceRecord Exception : ", e);
				e.printStackTrace();
			} catch (final IllegalAccessException e) {
				Log.d(LOG_TAG, "createRfcommSocketToServiceRecord Exception : ", e);
				e.printStackTrace();
			} catch (final InvocationTargetException e) {
				Log.d(LOG_TAG, "createRfcommSocketToServiceRecord Exception : ", e);
				e.printStackTrace();
			}

			// Cancel discovery because it will slow down the connection
			myBluetoothAdapter.cancelDiscovery();
			try {
				// Connect the device through the socket. This will block
				// until it succeeds or throws an exception
				mmSocket.connect();

			} catch (final IOException connectException) {
				Log.d(LOG_TAG, "connectException : ", connectException);
				// Unable to connect; close the socket and get out
				try {
					mmSocket.close();
				} catch (final IOException closeException) {
					Log.d(LOG_TAG, "closeException : ", closeException);
				}
				mmSocket = null;
			}

			// If a connection was accepted
			if (mmSocket != null) {
				try {
					if (ReadAsString) {
					} else {
						final DataInputStream dataInputStream = new DataInputStream(mmSocket.getInputStream());

						final int[] byteRecord = new int[12];
						int dyn_data = 0;
						int curr_byte_nbr = 0;
						while ((dyn_data = dataInputStream.readUnsignedByte()) >= 0 && continueRunning) {
							if (dyn_data == 254 && byteRecord[0] != 0) {
								hrr = PolarMessageParser.parseBytes(byteRecord);

								publishProgress(hrr);

								for (int i = 0; i < curr_byte_nbr; i++) {
									byteRecord[i] = 0;
								}
								curr_byte_nbr = 0;
							}
							byteRecord[curr_byte_nbr++] = dyn_data;

						}
					}
					try {
						mmSocket.close();
					} catch (final IOException closeException) {
						Log.d(LOG_TAG, "closeException : ", closeException);
					}
					mmSocket = null;
				} catch (final IOException e) {
					Log.d(LOG_TAG, "Comms Exception : ", e);
				}
			}
			return 1;
		}

		@Override
		protected void onProgressUpdate(final HeartRateRecord... values) {
			super.onProgressUpdate(values);
			runOnUiThread(new Runnable() {
				@Override
				public void run() {
					updateHeartRate(values[0]);
				}
			});
		}

		public void setContinueRunning(final Boolean value) {
			continueRunning = value;
		}
	}

	private static final String LOG_TAG = "PolarHRMonitor";
	private static final UUID MY_UUID = UUID.fromString("f2b90360-973f-11e0-aa80-0800200c9a66");
	private static final String NAME = "PolarHRMonitor";
	private static final String PREF_CURRENT_AGE = "PREF_CURRENT_AGE";
	private static final String PREF_MAX_HR = "PREF_MAX_HR";
	private static final String PREF_REST_HR = "PREF_REST_HR";
	private static final Boolean ReadAsString = false;

	private int age = 60;
	// The BroadcastReceiver that listens for discovered devices and
	// changes the title when discovery is finished
	private final BroadcastReceiver btDevFoundReceiver = new BroadcastReceiver() {
		@Override
		public void onReceive(final Context context, final Intent intent) {
			final String action = intent.getAction();

			// When discovery finds a device
			if (BluetoothDevice.ACTION_FOUND.equals(action)) {
				// Get the BluetoothDevice object from the Intent
				final BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
				// If it's already paired, skip it, because it's been listed
				// already
				if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
					mNewDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());
				}
				// When discovery is finished, change the Activity title
			} else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
				setProgressBarIndeterminateVisibility(false);
				setTitle(R.string.select_device);
				if (mNewDevicesArrayAdapter.getCount() == 0) {
					final String noDevices = getResources().getText(R.string.none_found).toString();
					mNewDevicesArrayAdapter.add(noDevices);
				}
			}
		}
	};
	private int eightyPercent = 0;

	private int fiftyPercent = 0;
	private final GpsLocationTask gpsTask = new GpsLocationTask();
	private int maximumHeartRate = 210;

	private ArrayAdapter<String> mNewDevicesArrayAdapter;

	private BluetoothAdapter myBluetoothAdapter;

	private int ninetyPercent = 0;
	private Set<BluetoothDevice> pairedDevices = null;
	private BluetoothDevice polarBtDev;
	private final PolarCommsTask polarCommsTsk = new PolarCommsTask();

	private int restingHeartRate = 60;

	private int seventyPercent = 0;
	private int sixtyPercent = 0;
	private TextView txtAge;

	private TextView txtHeartRate;
	private TextView txtRestingHeartRate;
	private TextView txtZone1From;

	private TextView txtZone1To;
	private TextView txtZone2From;
	private TextView txtZone2To;
	private TextView txtZone3From;
	private TextView txtZone3To;
	private TextView txtZone4From;
	private TextView txtZone4To;
	private TextView txtZone5From;

	private TextView txtZone5To;

	private void doCalculationKarvonen() {
		final int heartRateReserve = maximumHeartRate - restingHeartRate;
		final String hrRsrv = "Heart rate reserve is " + PolarMessageParser.nbrFmt.format(heartRateReserve)
				+ " bpm";

		fiftyPercent = (int) (heartRateReserve * 0.5 + restingHeartRate);
		sixtyPercent = (int) (heartRateReserve * 0.6 + restingHeartRate);
		seventyPercent = (int) (heartRateReserve * 0.7 + restingHeartRate);
		eightyPercent = (int) (heartRateReserve * 0.8 + restingHeartRate);
		ninetyPercent = (int) (heartRateReserve * 0.9 + restingHeartRate);

		txtZone1From.setText(PolarMessageParser.nbrFmt.format(fiftyPercent) + " bpm");
		txtZone1To.setText(PolarMessageParser.nbrFmt.format(sixtyPercent - 1) + " bpm");

		txtZone2From.setText(PolarMessageParser.nbrFmt.format(sixtyPercent) + " bpm");
		txtZone2To.setText(PolarMessageParser.nbrFmt.format(seventyPercent - 1) + " bpm");

		txtZone3From.setText(PolarMessageParser.nbrFmt.format(seventyPercent) + " bpm");
		txtZone3To.setText(PolarMessageParser.nbrFmt.format(eightyPercent - 1) + " bpm");

		txtZone4From.setText(PolarMessageParser.nbrFmt.format(eightyPercent) + " bpm");
		txtZone4To.setText(PolarMessageParser.nbrFmt.format(ninetyPercent - 1) + " bpm");

		txtZone5From.setText(PolarMessageParser.nbrFmt.format(ninetyPercent) + " bpm");
		txtZone5To.setText(PolarMessageParser.nbrFmt.format(maximumHeartRate) + " bpm");
	}

	private void doEstimateMaxHeartRate() {
		maximumHeartRate = 220 - age;
		maximumHeartRate = (int) (205.8 - 0.685 * age);
	}

	private boolean enableBluetooth() {
		myBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

		if (myBluetoothAdapter == null) {
			return false;
		}

		if (!myBluetoothAdapter.isEnabled()) {
			final Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
			startActivity(enableIntent);
		}
		return true;
	}

	/** Called when the activity is first created. */
	@Override
	public void onCreate(final Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.heart_rate_display);

		if (enableBluetooth()) {
			Log.e(LOG_TAG, "Bluetooth on :)");
			pairWithDevice();
			polarCommsTsk.execute(NAME);
		} else {
			Log.e(LOG_TAG, "Bluetooth won't turn on !!!");
			finish();
		}

		txtHeartRate = (TextView) findViewById(R.id.txtHeartRate);
		txtZone1From = (TextView) findViewById(R.id.zone1From);
		txtZone1To = (TextView) findViewById(R.id.zone1To);
		txtZone2From = (TextView) findViewById(R.id.zone2From);
		txtZone2To = (TextView) findViewById(R.id.zone2To);
		txtZone3From = (TextView) findViewById(R.id.zone3From);
		txtZone3To = (TextView) findViewById(R.id.zone3To);
		txtZone4From = (TextView) findViewById(R.id.zone4From);
		txtZone4To = (TextView) findViewById(R.id.zone4To);
		txtZone5From = (TextView) findViewById(R.id.zone5From);
		txtZone5To = (TextView) findViewById(R.id.zone5To);

		txtRestingHeartRate = (TextView) findViewById(R.id.txtRestingHeartRate);
		txtRestingHeartRate.addTextChangedListener(new TextWatcher() {
			@Override
			public void afterTextChanged(final Editable s) {
				updateValues();
				doEstimateMaxHeartRate();
				doCalculationKarvonen();
			}

			@Override
			public void beforeTextChanged(final CharSequence s, final int start, final int count,
					final int after) {
				// do nothing
			}

			@Override
			public void onTextChanged(final CharSequence s, final int start, final int before, final int count) {
				// do nothing
			}
		});

		txtAge = (TextView) findViewById(R.id.txtAge);
		txtAge.addTextChangedListener(new TextWatcher() {
			@Override
			public void afterTextChanged(final Editable s) {
				updateValues();
				doEstimateMaxHeartRate();
				doCalculationKarvonen();
			}

			@Override
			public void beforeTextChanged(final CharSequence s, final int start, final int count,
					final int after) {
				// do nothing
			}

			@Override
			public void onTextChanged(final CharSequence s, final int start, final int before, final int count) {
				// do nothing
			}
		});

		preferencesLoad();
		txtAge.setText("" + age);
		txtRestingHeartRate.setText("" + restingHeartRate);
		updateValues();
		doEstimateMaxHeartRate();
		doCalculationKarvonen();
	}

	@Override
	protected void onPause() {
		super.onPause();
		preferencesSave();
		polarCommsTsk.setContinueRunning(false);
		polarCommsTsk.cancel(true);

		gpsTask.setContinueRunning(false);
		gpsTask.cancel(true);
	}

	@Override
	protected void onStop() {
		super.onStop();
	}

	private void pairWithDevice() {
		pairedDevices = myBluetoothAdapter.getBondedDevices();

		for (final BluetoothDevice dev : pairedDevices) {
			Log.d(LOG_TAG, "BT device: [name :" + dev.getName() + "; addr:" + dev.getAddress() + "; BtClass:"
					+ dev.getBluetoothClass() + "; bonded:" + dev.getBondState());
			if (dev.getName().toLowerCase().contains("polar")) {
				polarBtDev = dev;
			}
		}

	}

	private void preferencesLoad() {
		final SharedPreferences mPrefs = getSharedPreferences(LOG_TAG, Context.MODE_PRIVATE);
		age = mPrefs.getInt(PREF_CURRENT_AGE, 21);
		restingHeartRate = mPrefs.getInt(PREF_REST_HR, 60);
	}

	private void preferencesSave() {
		try {
			final SharedPreferences mPrefs = getSharedPreferences(LOG_TAG, Context.MODE_PRIVATE);
			final SharedPreferences.Editor ed = mPrefs.edit();
			ed.putInt(PREF_CURRENT_AGE, age);
			ed.putInt(PREF_REST_HR, restingHeartRate);
			ed.commit();
		} catch (final Exception e) {
			// ignore error
		}
	}

	private void updateCurrentLocation(final Location myLoc) {
	}

	private void updateHeartRate(final HeartRateRecord hrr) {
		txtHeartRate.setText("" + hrr.heartRate);
		if (hrr.heartRate < sixtyPercent) {
			txtHeartRate.setTextColor(getResources().getColor(R.color.zone1));
		} else if (hrr.heartRate < seventyPercent) {
			txtHeartRate.setTextColor(getResources().getColor(R.color.zone2));
		} else if (hrr.heartRate < eightyPercent) {
			txtHeartRate.setTextColor(getResources().getColor(R.color.zone3));
		} else if (hrr.heartRate < ninetyPercent) {
			txtHeartRate.setTextColor(getResources().getColor(R.color.zone4));
		} else {
			txtHeartRate.setTextColor(getResources().getColor(R.color.zone5));
		}
	}

	private void updateValues() {
		final String rhrStr = txtRestingHeartRate.getText().toString();
		final String ageStr = txtAge.getText().toString();
		try {
			restingHeartRate = Integer.parseInt(rhrStr);
		} catch (final Exception e) {
			restingHeartRate = 60;
		}
		try {
			age = Integer.parseInt(ageStr);
		} catch (final Exception e) {
			age = 21;
		}
	}
}